package com.lvhx.redis;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;

import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
public class MybatisRedisCache implements Cache {
    private String id;
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private static RedisTemplate redisTemplate;
    private static final long EXPIRE_TIME_IN_MINUTES = 3; // redis过期时间

    public MybatisRedisCache() {
    }

    public MybatisRedisCache(String id) {
        log.info("=====缓存ID = " + id);
        this.id = id;
    }

    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        MybatisRedisCache.redisTemplate = redisTemplate;
    }

    private RedisTemplate<Object, Object> getRedisTemplate() {
        return ApplicationContextHolder.getBean("redisTemplate");
    }

    @Override
    public String getId() {
        return id;
    }

    /**
     * 如果缓存里没有数据
     * 则走完查询逻辑后，会走这边
     *
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        RedisTemplate redisTemplate = getRedisTemplate();
        StopWatch stopWatch = new StopWatch("putObject");
        stopWatch.start();
        String field = getHFieldKey(key);

        try {
            // boundHashOps方法是返回一个绑定了指定键的BoundHashOperations对象
            redisTemplate.boundHashOps(getId()).put(field, value);
            // 为整个Hash设置过期时间
            redisTemplate.expire(getId(), EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
            log.info("=====结果放入到缓存中: {}---{}", getId(), field);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("=====结果放入缓存失败: " + key + "=" + value);
        }
        stopWatch.stop();
        log.info("=====putObject耗时统计:\n{}", stopWatch.prettyPrint());

    }

    /**
     * key未加密之前是这样的：
     * "144644107:2377769587:com.lvhx.dao.extend.OrderExtednMapper.getAllInfo:0:2147483647:select * from t_order:SqlSessionFactoryBean"
     */
    private String getHFieldKey(Object key) {
        StringBuilder sb = new StringBuilder();
        // 加密后是一个32位的字符串
//        sb.append(DigestUtils.md5Hex(String.valueOf(key)));

        sb.append(key);
        return sb.toString();
    }

    /**
     * 在执行查询sql之前会走这边
     *
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        RedisTemplate redisTemplate = getRedisTemplate();
        StopWatch stopWatch = new StopWatch("getObject");
        stopWatch.start();
        String field = getHFieldKey(key);
        try {
            Object value = redisTemplate.boundHashOps(getId()).get(field);
            log.info("=====从缓存中获取了: {}", value);
            return value;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("[=====从缓存获取失败: " + key + " ]");
        }
        stopWatch.stop();
        log.info("=====getObject耗时统计:\n{}", stopWatch.prettyPrint());
        return null;
    }

    /**
     * 事务回滚后会调用这个方法
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        Object value = null;
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            String field = getHFieldKey(key);
            value = redisTemplate.boundHashOps(getId()).delete(field);
            log.info("=====从缓存删除了: {}", field);
        } catch (Exception e) {
            log.error("=====从缓存删除失败: " + key);
        }
        return value;
    }

    /**
     * 正常更新mapper后，会调用这个方法
     * 更新完成后会在调用putObject方法放入一个空值
     * 下一次查询缓存发现是null，会再从数据库查询
     */
    @Override
    public void clear() {
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.delete(getId());
//            int i = 1/0;
            log.info("=====清空{}缓存!!!", getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getSize() {
        RedisTemplate redisTemplate = getRedisTemplate();
        Long size = redisTemplate.boundHashOps(getId()).size();
        return size == null ? 0 : size.intValue();
    }

    /**
     * 清理所有缓存
     */
    public void clearAll() {
        RedisTemplate redisTemplate = getRedisTemplate();
        Set keys = redisTemplate.keys("*");
        if (!ObjectUtils.isEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 用于保证线程安全的，这个版本应该已经不用了
     *
     * @return
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
}
